package org.mili.ticketeer.report;

import org.apache.commons.lang.StringEscapeUtils;
import org.mili.core.templating.TemplateStore;
import org.mili.ticketeer.dto.Action;
import org.mili.ticketeer.dto.Contact;
import org.mili.ticketeer.dto.ContactRole;
import org.mili.ticketeer.dto.Group;
import org.mili.ticketeer.dto.Meeting;
import org.mili.ticketeer.dto.Priority;
import org.mili.ticketeer.dto.Role;
import org.mili.ticketeer.dto.SexEnum;
import org.mili.ticketeer.dto.Ticket;
import org.mili.ticketeer.service.ActionService;
import org.mili.ticketeer.service.ContactService;
import org.mili.ticketeer.service.GroupService;
import org.mili.ticketeer.service.MeetingService;
import org.mili.ticketeer.service.PriorityService;
import org.mili.ticketeer.service.RoleService;
import org.mili.ticketeer.service.ServiceException;
import org.mili.ticketeer.service.ServiceFactory;
import org.mili.ticketeer.service.StateService;
import org.mili.ticketeer.service.TicketService;

import java.io.IOException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * Created with IntelliJ IDEA.
 * User: micha
 * Date: 26.07.13
 * Time: 14:17
 * To change this template use File | Settings | File Templates.
 */
public class ReportEngine {
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy");
    private TemplateStore templateStore = new TemplateStore();
    private TicketService ticketService = ServiceFactory.getService(TicketService.class);
    private ActionService actionService = ServiceFactory.getService(ActionService.class);
    private ContactService contactService = ServiceFactory.getService(ContactService.class);
    private RoleService roleService = ServiceFactory.getService(RoleService.class);
    private StateService stateService = ServiceFactory.getService(StateService.class);
    private GroupService groupService = ServiceFactory.getService(GroupService.class);
    private PriorityService priorityService = ServiceFactory.getService(PriorityService.class);
    private MeetingService meetingService = ServiceFactory.getService(MeetingService.class);

    public ReportEngine() {
        templateStore.read("./src/main/resources/templates/tickets_high.html", "tickets_high");
        templateStore.read("./src/main/resources/templates/tickets_request.html", "tickets_request");
        templateStore.read("./src/main/resources/templates/tickets_followup.html", "tickets_followup");
        templateStore.read("./src/main/resources/templates/tickets_open.html", "tickets_open");
        templateStore.read("./src/main/resources/templates/tickets_long.html", "tickets_long");
        templateStore.read("./src/main/resources/templates/tickets_closed.html", "tickets_closed");
        templateStore.read("./src/main/resources/templates/tickets_rejected.html", "tickets_rejected");
        templateStore.read("./src/main/resources/templates/members.html", "members");
        templateStore.read("./src/main/resources/templates/functional.html", "functional");
        templateStore.read("./src/main/resources/templates/meetings.html", "meetings");
    }


    public void start() throws ServiceException {
        try {
            new NameListGenerator(this, templateStore).generate();
            new LongDurationTicketGenerator(this, templateStore).generate();
            new HighPrioTicketGenerator(this, templateStore).generate();
            new RequestTicketGenerator(this, templateStore).generate();
            new FollowUpTicketGenerator(this, templateStore).generate();
            new OpenTicketGenerator(this, templateStore).generate();
            new ClosedTicketGenerator(this, templateStore).generate();
            new RejectedTicketGenerator(this, templateStore).generate();
            new MeetingGenerator(this, templateStore).generate();
            new FunctionalListGenerator(this, templateStore).generate();
        } catch (IOException e) {
            throw new ServiceException(e);
        }
    }

    public String printSex(SexEnum sex) {
        switch (sex) {
            case MALE:
                return "Herr";
            case FEMALE:
                return "Frau";
            default:
                throw new IllegalArgumentException("no sex!");
        }
    }

    public String getHtml(String s) {
        return StringEscapeUtils.escapeHtml(s);
    }

    public String getHtml(Timestamp date) {
        return StringEscapeUtils.escapeHtml(date != null ? simpleDateFormat.format(date) : "-/-");
    }

    public String replaceWithTicket(String s, Ticket ticket) throws ServiceException {
        Group group = groupService.findById(ticket.getGroup());
        return s.replace("${ticket_name}", getHtml(ticket.getTitle()))
                .replace("${ticket_priority}", getTicketPriority(ticket))
                .replace("${ticket_state}", getTicketState(ticket))
                .replace("${ticket_created}", getHtml(ticket.getCreatedAt()))
                .replace("${ticket_finished}", getHtml(ticket.getCompletedAt()))
                .replace("${ticket_contact}", getHtml(getContacts(ticketService.getAssignmentsFor(ticket))))
                .replace("${ticket_description}", getTicketDescription(ticket))
                .replace("${ticket_group}", group.getName());
    }

    public List<Contact> getContacts() throws ServiceException {
        List<Contact> list = contactService.getContacts();
        Collections.sort(list, new Comparator<Contact>() {
            @Override
            public int compare(Contact o1, Contact o2) {
                return (o1.getLastName() + o1.getFirstName()).compareTo(o2.getLastName()
                        + o2.getFirstName());
            }
        });
        return list;
    }

    public Map<Role, List<Contact>> getSpeakersByRole() throws ServiceException {
        Map<Role, List<Contact>> roles = new HashMap<>();
        for (Contact contact : contactService.getContacts()) {
            List<ContactRole> list = roleService.getAssignedContactRolesFor(contact);
            for (ContactRole contactRole : list) {
                System.out.println("contact: " + contact.getLastName() + " --> " + contactRole.getRole());
                Role role = roleService.findRoleById(contactRole.getRole());
                List<Contact> contacts = roles.get(role);
                if (contacts == null) {
                    contacts = new ArrayList<>();
                    roles.put(role, contacts);
                }
                contacts.add(contact);
            }
        }
        return roles;
    }

    public Map<Role, List<Contact>> getGroupRoles() throws ServiceException {
        Map<Role, List<Contact>> roles = new TreeMap<>();
        for(Map.Entry<Role, List<Contact>> entry : getSpeakersByRole().entrySet()) {
            if (entry.getValue().size() > 1) {
                roles.put(entry.getKey(), entry.getValue());
            }
        }
        return roles;
    }

    public Map<Role, Contact> getSingleRoles() throws ServiceException {
        Map<Role, Contact> roles = new TreeMap<>();
        for(Map.Entry<Role, List<Contact>> entry : getSpeakersByRole().entrySet()) {
            if (entry.getValue().size() == 1) {
                roles.put(entry.getKey(), entry.getValue().get(0));
            }
        }
        return roles;
    }

    public String getTicketState(Ticket ticket) throws ServiceException {
        org.mili.ticketeer.dto.State state = stateService.findById(ticket.getState());
        if (state == null) {
            throw new IllegalArgumentException("no state!");
        }
        return getHtml(state.getTitle());
    }

    public String getTicketPriority(Ticket ticket) throws ServiceException {
        Priority priority = priorityService.findById(ticket.getPriority());
        if (priority == null) {
            return "-/-";
        }
        return getHtml(priority.getName());
    }

    public String getTicketDescription(Ticket ticket) throws ServiceException {
        StringBuilder s = new StringBuilder();
        StringBuilder actions = new StringBuilder();
        actions.append("<OL>");
        final String TEMP = "%s&nbsp;(%s)&nbsp;%s - %s";
        s.append(getHtml(ticket.getComment()));
        List<Action> actionList = actionService.getActionsFor(ticket);
        for (Action action : actionList) {
            actions.append("<LI>");
            actions.append(String.format(TEMP, getHtml(action.getDate()), getHtml(getContacts(actionService.getAssignmentsFor(
                    action))),
                    getHtml(action.getTitle()), getHtml(action.getComment())));
            actions.append("</LI>");
        }
        if (actionList.size() > 0) {
            actions.append("</OL>");
            s.append(actions);
        }
        return s.toString();
    }

    private String getContacts(List<Contact> contacts) throws ServiceException {
        StringBuilder s = new StringBuilder();
        for (int i = 0, n = contacts.size(); i < n; i ++) {
            Contact contact = contacts.get(i);
            s.append(contact.getLastName());
            if (i < n - 1) {
                s.append(", ");
            }
        }
        return s.toString();
    }

    public static void main(String[] args) throws ServiceException {
        new ReportEngine().start();
    }

    public Map<String, Map<String, Set<Contact>>> getSpeakersByStreet() throws ServiceException {
        Map<String, Map<String, Set<Contact>>> speakersByStreet = new TreeMap<>();
        for(Contact contact : contactService.getContacts()) {
            Map<String, Set<Contact>> map = speakersByStreet.get(contact.getStreet());
            if (map == null) {
                map = new TreeMap<>();
                speakersByStreet.put(contact.getStreet(), map);
            }
            Set<Contact> set = map.get(contact.getNumber());
            if (set == null) {
                set = new HashSet<>();
            }
            set.add(contact);
            map.put(contact.getNumber(), set);
        }

        return speakersByStreet;
    }

    public Map<Group, List<Ticket>> getTicketsByGroups() throws ServiceException {
        Map<Group, List<Ticket>> map = new HashMap<>();
        for (Ticket ticket : ticketService.getTickets()) {
            Group group = groupService.findById(ticket.getPriority());
            List<Ticket> list = map.get(group);
            if (list == null) {
                list = new ArrayList<>();
                map.put(group, list);
            }
            list.add(ticket);
        }
        return map;
    }

    public List<Ticket> getOpenTickets(List<Ticket> tickets) throws ServiceException {
        List<Ticket> result = new ArrayList<>();
        for(Ticket ticket : tickets) {
            org.mili.ticketeer.dto.State state = stateService.findById(ticket.getState());
            if (!"DCRF".contains(state.getToken())) {
                result.add(ticket);
            }
        }
        sortTickets(result);
        return result;
    }

    public List<Ticket> getClosedTickets(List<Ticket> tickets) throws ServiceException {
        List<Ticket> result = new ArrayList<>();
        for(Ticket ticket : tickets) {
            org.mili.ticketeer.dto.State state = stateService.findById(ticket.getState());
            if ("D".contains(state.getToken())) {
                result.add(ticket);
            }
        }
        sortTickets(result);
        return result;
    }

    public List<Ticket> getRejectedTickets(List<Ticket> tickets) throws ServiceException {
        List<Ticket> result = new ArrayList<>();
        for(Ticket ticket : tickets) {
            org.mili.ticketeer.dto.State state = stateService.findById(ticket.getState());
            if ("C".contains(state.getToken())) {
                result.add(ticket);
            }
        }
        sortTickets(result);
        return result;
    }

    public List<Ticket> getLongDurationTickets() throws ServiceException {
        List<Ticket> tickets = ticketService.getTickets();
        for(int i = tickets.size() - 1; i >= 0; i --) {
            Ticket ticket = tickets.get(i);
            org.mili.ticketeer.dto.State state = stateService.findById(ticket.getState());
            if ("DC".contains(state.getToken())) {
                tickets.remove(i);
            }
        }
        sortLongTickets(tickets);
        return tickets;
    }

    public List<Ticket> getHighPrioTickets(List<Ticket> tickets) throws ServiceException {
        List<Ticket> result = new ArrayList<>();
        for(Ticket ticket : tickets) {
            org.mili.ticketeer.dto.State state = stateService.findById(ticket.getState());
            org.mili.ticketeer.dto.Priority priority = priorityService.findById(ticket.getPriority());
            if ("R".contains(state.getToken()) && priority.getPriority() == 3) {
                result.add(ticket);
            }
        }
        sortLongTickets(result);
        return result;
    }

    public List<Ticket> getRequestTickets(List<Ticket> tickets) throws ServiceException {
        List<Ticket> result = new ArrayList<>();
        for(Ticket ticket : tickets) {
            org.mili.ticketeer.dto.State state = stateService.findById(ticket.getState());
            org.mili.ticketeer.dto.Priority priority = priorityService.findById(ticket.getPriority());
            if (!"DC".contains(state.getToken()) && "R".contains(state.getToken()) && priority.getPriority() != 3) {
                result.add(ticket);
            }
        }
        sortLongTickets(result);
        return result;
    }

    public List<Ticket> getFollowUpTickets(List<Ticket> tickets) throws ServiceException {
        List<Ticket> result = new ArrayList<>();
        for(Ticket ticket : tickets) {
            org.mili.ticketeer.dto.State state = stateService.findById(ticket.getState());
            org.mili.ticketeer.dto.Priority priority = priorityService.findById(ticket.getPriority());
            if (!"DC".contains(state.getToken()) && "F".contains(state.getToken())) {
                result.add(ticket);
            }
        }
        sortLongTickets(result);
        return result;
    }

    private void sortTickets(List<Ticket> tickets) {
        Collections.sort(tickets, new Comparator<Ticket>() {
            @Override
            public int compare(Ticket o1, Ticket o2) {
                int p1 = getPriority(o1);
                int p2 = getPriority(o2);
                int i = p2 - p1;
                if (i == 0) {
                    long l1 = o1.getCreatedAt().getTime();
                    long l2 = o2.getCreatedAt().getTime();
                    long l = l1 - l2;
                    if (l < 0) {
                        return 1;
                    } else if (l > 0) {
                        return -1;
                    } else {
                        return o1.getTitle().compareTo(o2.getTitle());
                    }
                }
                return i;
            }
        });
    }

    private void sortLongTickets(List<Ticket> tickets) {
        Collections.sort(tickets, new Comparator<Ticket>() {
            @Override
            public int compare(Ticket o1, Ticket o2) {
                long l1 = o1.getCreatedAt().getTime();
                long l2 = o2.getCreatedAt().getTime();
                long l = l2 - l1;
                if (l < 0) {
                    return 1;
                } else if (l > 0) {
                    return -1;
                } else {
                    int p1 = getPriority(o1);
                    int p2 = getPriority(o2);
                    int i = p2 - p1;
                    if (i == 0) {
                        return o1.getTitle().compareTo(o2.getTitle());
                    }
                }
                return (int) l;
            }
        });
    }

    private int getPriority(Ticket o1) {
        try {
            Priority p = priorityService.findById(o1.getPriority());
            if (p == null) {
                return -1;
            }
            return p.getPriority();
        } catch (ServiceException e) {
        }
        return -1;
    }

    public Set<Integer> getCurrentMeetings(Contact contact) throws ServiceException {
        Set<Integer> set = new HashSet<>();
        for(Meeting meeting : meetingService.getVisitedMeetingsFor(contact, 2013)) {
            String dateString = String.valueOf(meeting.getDate());
            String dayString = dateString.substring(4, 6);
            System.out.println("-> " + dayString);
            set.add(Integer.parseInt(dayString));
        }
        return set;
    }

}
